package cloud.tianai.mate.captcha.interceptor.impl;

import cloud.tianai.captcha.common.AnyMap;
import cloud.tianai.captcha.common.response.ApiResponse;
import cloud.tianai.captcha.common.response.CodeDefinition;
import cloud.tianai.captcha.common.util.CaptchaTypeClassifier;
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
import cloud.tianai.captcha.interceptor.Context;
import cloud.tianai.captcha.validator.common.model.dto.ImageCaptchaTrack;
import cloud.tianai.mate.captcha.interceptor.group.FeaturesCaptchaInterceptorGroup;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringJoiner;
import java.util.stream.Collectors;

/**
 * @Author: 天爱有情
 * @date 2024/7/12 10:27
 * @Description 验证码滑动轨迹相似性校验
 */
public class SliderTrackSimilarityCaptchaInterceptor implements CaptchaInterceptor {
    public static final CodeDefinition DEFINITION = new CodeDefinition(60004, "Similarity check fail");
    public static final String SIMILARITY_X_COUNT_KEY = "similarity_x_count";

    @Override
    public String getName() {
        return "slider_track_similarity_check";
    }

    @Override
    public ApiResponse<?> afterValid(Context context, String type, ImageCaptchaTrack imageCaptchaTrack, AnyMap validData, ApiResponse<?> basicValid) {
        ApiResponse<?> preResult = CaptchaInterceptor.super.beforeValid(context, type, imageCaptchaTrack, validData);
        if (!preResult.isSuccess()) {
            return preResult;
        }
        if (!basicValid.isSuccess()) {
            return context.getGroup().afterValid(context, type, imageCaptchaTrack, validData, basicValid);
        }
        AnyMap featureMap = context.getData(FeaturesCaptchaInterceptorGroup.DATA_FEATURES, AnyMap.class);
        if (!CaptchaTypeClassifier.isSliderCaptcha(type) || featureMap == null) {
            return ApiResponse.ofSuccess();
        }

        // 判断当前用户验证码轨迹相似性
        List<ImageCaptchaTrack.Track> trackList = imageCaptchaTrack.getTrackList();

        // 按照x为10个为一组
        List<List<ImageCaptchaTrack.Track>> groupList = new ArrayList<>();
        int window = 1;
        float factor = 3;
        List<ImageCaptchaTrack.Track> list = new ArrayList<>();
        groupList.add(list);
        ImageCaptchaTrack.Track startTrack = trackList.get(0);
        for (ImageCaptchaTrack.Track track : trackList) {

            float x = window * 20;
            float x1 = track.getX() - startTrack.getX();
            if (x1 > x) {
                list = new ArrayList<>();
                groupList.add(list);
                window++;
            }
            list.add(track);
        }
        List hisXCounts = (List) featureMap.getOrDefault(SIMILARITY_X_COUNT_KEY, new ArrayList<>());
        List<List<List<Float>>> hisXCountsList = new ArrayList<>();
        for (Object hisXCount : hisXCounts) {
            String str = hisXCount.toString();
            List<List<Float>> list1 = Arrays.stream(str.split(","))
                    .map(s -> Arrays.stream(s.split(":")).map(Float::parseFloat).collect(Collectors.toList()))
                    .collect(Collectors.toList());
            hisXCountsList.add(list1);
        }

        int xHitCount = 0;
        int yHitCount = 0;
        int tHitCount = 0;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < groupList.size(); i++) {
            List<ImageCaptchaTrack.Track> splitTracks = groupList.get(i);
            int xCount = splitTracks.size();
            float minY = splitTracks.get(0).getY();
            float maxY = splitTracks.get(0).getY();
            float minTime = splitTracks.get(0).getT();
            float maxTime = splitTracks.get(splitTracks.size() - 1).getT();
            for (ImageCaptchaTrack.Track splitTrack : splitTracks) {
                if (splitTrack.getY() < minY) {
                    minY = splitTrack.getY();
                } else if (splitTrack.getY() > maxY) {
                    maxY = splitTrack.getY();
                }
            }
            float y = maxY - minY;
            float t = maxTime - minTime;

            boolean xHit = false;
            boolean yHit = false;
            boolean tHit = false;
            for (List<List<Float>> his : hisXCountsList) {
                if (his.size() > i) {
                    if (!xHit && his.get(i).get(0) == splitTracks.size()) {
                        xHitCount++;
                        xHit = true;
                    }
                    if (!yHit && his.get(i).get(1) == y) {
                        yHitCount++;
                        yHit = true;
                    }
                    if (!tHit && his.get(i).get(2) == t) {
                        tHitCount++;
                        tHit = true;
                    }
                    if (xHit && yHit && tHit) {
                        break;
                    }
                }
            }
            sb.append(xCount).append(":").append(y).append(":").append(t).append(",");
        }
//        System.out.println("xHit:" + ((float) xHitCount / groupList.size()));
//        System.out.println("yHit:" + (float) yHitCount / groupList.size());
//        System.out.println("tHit:" + (float) tHitCount / groupList.size());

        float xHit = (float) xHitCount / groupList.size();
        float yHit = (float) yHitCount / groupList.size();
        float tHit = (float) tHitCount / groupList.size();
        if (xHit >= 0.95 && yHit >= 0.95 && tHit >= 0.95) {
            // 直接禁掉
            featureMap.put(DisableCaptchaInterceptor.DISABLE_KEY, true);
            context.end();
            return ApiResponse.ofMessage(DEFINITION);
        }
        if (xHit > 0.7) {
            context.end();
            return ApiResponse.ofMessage(DEFINITION);
        }
        if (yHit > 0.9) {
            context.end();
            return ApiResponse.ofMessage(DEFINITION);
        }
        if (tHit > 0.7) {
            context.end();
            return ApiResponse.ofMessage(DEFINITION);
        }


        StringJoiner stringJoiner = new StringJoiner(",");
        for (List<ImageCaptchaTrack.Track> splitTracks : groupList) {
            stringJoiner.add(splitTracks.size() + "");
        }
        hisXCounts.add(sb.substring(0, sb.length() - 1));
        while (hisXCounts.size() > 5) {
            hisXCounts.remove(0);
        }
        featureMap.put(SIMILARITY_X_COUNT_KEY, hisXCounts);
        return ApiResponse.ofSuccess();
    }
}
